package Navi_run;

import encryption.Hash;
import it.unisa.dia.gas.jpbc.Element;
import objects.LatLonPoint;
import objects.LocationBean;
import org.ujmp.core.Matrix;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;

import static Navi_run.TA_driver.Ei1;
import static Navi_run.params.*;
import static Navi_run.params.lshMinHash;
import static encryption.Fuzzy_search.*;
import static encryption.Opaak.*;
import static parameter.parameter_length.r1r2_length;

public class Navigation_query {
    //司机生成查询请求索引Index_Enc(KI) 如果flag==true 就是请求司机的EncIndex 否则是贡献司机EncQuery
    public static double[][] driver_generate_query(List<LocationBean> locationBeans, boolean flag) {
        byte[] key = null;
        double[][] EncKI = new double[locationBeans.size() * 2][(int) TA_driver.driver_V.getColumnCount()];
        System.out.println(locationBeans.size());
        for (int i = 0; i < locationBeans.size() * 2; i = i + 2) {
            System.out.println(i);
            double[] I = BuildIndex(locationBeans.get(i/2), key, lshMinHash);
            Map<String, Matrix> EncIndex;
            if (flag == true) {
                EncIndex = Enc_Index(TA_driver.SK1, TA_driver.SK2, TA_driver.driver_V, I);
            } else {
                EncIndex = Enc_Query(TA_driver.SK1, TA_driver.SK2, TA_driver.driver_V, I);
            }
            //把加密结果EncIndex保存到EncKI中
            Matrix SK1_I1 = EncIndex.get("SK1_I1").transpose();
            Matrix SK2_I2 = EncIndex.get("SK2_I2").transpose();
            for (int j = 0; j < SK1_I1.getColumnCount(); j++) {
                EncKI[i][j] = SK1_I1.getAsDouble(0, j);
                EncKI[i + 1][j] = SK2_I2.getAsDouble(0, j);
            }
        }
        return EncKI;
    }

    //零知识证明的一个测试
    public static void ZKPK_test(String message, BigInteger si) {
        //TA生成对司机的签名
        TA_driver.TAtoDriver_ClSign = generateZKPKSign(si, params.clSign_PK_d1, params.clSign_PK_d2, params.clSign_PK_d3, params.clSign_PK_n, params.clSign_SK_p);
        //司机生成对si的零知识证明
        TA_driver.DriverToNsp_ZKPK = generateZPKP(message, si, params.Zr, TA_driver.TAtoDriver_ClSign, params.ZKPK_b, params.ZKPK_F, params.clSign_PK_n);
        //NSP验证零知识证明
        boolean flag = verifyZKPK(message, TA_driver.DriverToNsp_ZKPK, si, params.clSign_PK_d1, params.clSign_PK_d2, params.clSign_PK_d3, params.clSign_PK_n, params.ZKPK_b, params.ZKPK_F);
        System.out.println(flag);
    }

    //司机端计算NQ,这里的te是时间历元 te
    public static Map<String, Object> driver_generate_NQ(String te, int rci, BigInteger si, double[][] EncKI, boolean flag) {
        //TA生成对司机的签名
        TA_driver.TAtoDriver_ClSign = generateZKPKSign(si, params.clSign_PK_d1, params.clSign_PK_d2, params.clSign_PK_d3, params.clSign_PK_n, params.clSign_SK_p);
        //司机生成对si的零知识证明
        TA_driver.DriverToNsp_ZKPK = generateZPKP(te, si, params.Zr, TA_driver.TAtoDriver_ClSign, params.ZKPK_b, params.ZKPK_F, params.clSign_PK_n);
        String Mi = Arrays.deepToString(EncKI) + TA_driver.DriverToNsp_ZKPK.get("rlpi");
        Rsu_driver.rlpi = TA_driver.DriverToNsp_ZKPK.get("rlpi");

        //随机选择r1讲(Ei1,Ei2)随机化
        SecureRandom secureRandom = new SecureRandom();
        BigInteger r1 = new BigInteger(r1r2_length, secureRandom);
        Rsu_driver.r1 = r1;
        Element REi1 = TA_driver.driver_Ei1.pow(r1).getImmutable();
        Element REi2 = TA_driver.driver_Ei2.pow(r1).getImmutable();
        //随机选择r2 计算e(E0i1, B)^r2 ← e(Ei1, B)^r1r2
        BigInteger r2 = new BigInteger(r1r2_length, secureRandom);
        Rsu_driver.r2 = r2;

        // 计算Ei = e(REi1, B)^r2
        Element Ei = pairing.pairing(REi1, gpk_B).pow(r2).getImmutable();
        Element Eii = pairing.pairing(TA_driver.driver_Ei1,gpk_B).pow(r1.multiply(r2));
        //计算ci = H(REi1, REi2, e(REi1, B)^r2, Mi)
        String temp = REi1.toString() + REi2.toString() + Ei.toString() + Mi + rci;
        BigInteger ci = new BigInteger(Hash.sha256(temp));
        //计算ssi = r2 + cisi
        BigInteger ssi = r2.add(ci.multiply(TA_driver.si));
        //建立NQ 并发送给rsu/NSP
        Map<String, Object> NQ = new HashMap<>();
        Rsu_driver.NQ = NQ;

        NQ.put("Mi", Mi);
        if (flag == false) {
            NQ.put("Query", EncKI);
        }
        NQ.put("REi1", REi1);
        NQ.put("REi2", REi2);
        NQ.put("ci", ci);
        NQ.put("ssi", ssi);
        Rsu_driver.Mi = Mi;
        Rsu_driver.REi1 = REi1.getImmutable();
        Rsu_driver.REi2 = REi2.getImmutable();
        Rsu_driver.ci = ci;
        Rsu_driver.ssi = ssi;
//        System.out.println(NQ.get("Mi"));
//        System.out.println("REi1:" + NQ.get("REi1"));
//        System.out.println("REi2:" + NQ.get("REi2"));
//        System.out.println("ci:" + NQ.get("ci"));
//        System.out.println(NQ.get("ssi"));
        Rsu_driver.rci = rci;
        TA_driver.rci++;

        Element Eiiii = (params.pairing.pairing(Rsu_driver.REi1, params.gpk_A).pow(Rsu_driver.ci)).mul(
                (params.pairing.pairing(Rsu_driver.REi2, params.gpk_g).pow(Rsu_driver.ci)).negate()
        ).mul(params.pairing.pairing(Rsu_driver.REi1, params.gpk_B).pow(Rsu_driver.ssi));
        return NQ;
    }

    //本模块测试用函数
    public static void main(String[] args) throws Exception {
        initialize initialize = new initialize();
        entity_register.driverSendRegisterRequest();
        entity_register.TaResponseDriverRegist();
        entity_register.driver_receive();
        List<LocationBean> locationBeans = new ArrayList<LocationBean>();
        LocationBean locationBean = new LocationBean(35.665553, 48.6666456);
        locationBeans.add(locationBean);
        locationBean.setLat(35.665543);
        locationBean.setLng(35.665543);
        locationBeans.add(locationBean);
        driver_generate_NQ("12345678", TA_driver.rci, TA_driver.si, driver_generate_query(locationBeans, true), true);
    }

    //被主函数调用
    public static void transition() throws Exception {
        initialize initialize = new initialize();
        entity_register.driverSendRegisterRequest();
        entity_register.TaResponseDriverRegist();
        entity_register.driver_receive();

        List<LocationBean> locationBeans = new ArrayList<LocationBean>();
        LocationBean locationBean = new LocationBean(35.665553, 48.6666456);
        locationBeans.add(locationBean);
        locationBean.setLat(35.665543);
        locationBean.setLng(35.665543);
        locationBeans.add(locationBean);
        driver_generate_NQ("12345678", TA_driver.rci, TA_driver.si, driver_generate_query(locationBeans, true), true);
    }
}
